package xyz.zhzby.cyzx.oj.service.biz;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import io.swagger.annotations.ApiOperation;
import lombok.EqualsAndHashCode;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.concurrent.CopyOnWriteArraySet;

/**
 * @author zby
 * @date 2022/02/06
 */
@ApiModel("实时推送工具类（页面级）")
@ServerEndpoint(value = "/oj/problem/submission/{userId}/{pageId}")
@Component
@EqualsAndHashCode
@Slf4j
public class ProblemWebSocketService {

    @ApiModelProperty("静态变量，用来记录当前在线连接数。应该把它设计成线程安全的。")
    private static int onlineCount = 0;

    @ApiModelProperty("concurrent包的线程安全Set，用来存放每个客户端对应的MyWebSocket对象。")
    private static final CopyOnWriteArraySet<ProblemWebSocketService> WEB_SOCKET_SET = new CopyOnWriteArraySet<>();

    @ApiModelProperty("与某个客户端的连接会话，需要通过它来给客户端发送数据")
    private Session session;

    @ApiModelProperty("某个客户端的连接对应的用户ID，需要用它选择性发送数据")
    private Long userId;

    @ApiModelProperty("某个客户端的连接对应的试题页面ID，需要用它选择性发送数据")
    private Long pageId;

    @ApiOperation("连接成功调用的方法")
    @OnOpen
    public void onOpen(@PathParam("userId") Long userId, @PathParam("pageId") Long pageId, Session session) {
        this.session = session;
        this.userId = userId;
        this.pageId = pageId;
        //加入到Set中
        WEB_SOCKET_SET.add(this);
        //在线试题页面加一
        addOnlineCount();
        log.info("有新用户ID为：{}的试题页面ID为：{}的连接加入，当前在线试题页面为：{}", userId, pageId, getOnlineCount());
    }

    @ApiOperation("连接关闭调用的方法")
    @OnClose
    public void onClose() {
        //从Set中删除
        WEB_SOCKET_SET.remove(this);
        //在线试题页面减一
        subOnlineCount();
        log.info("用户ID为：{}的试题页面ID为：{}的连接关闭，当前在线试题页面为：{}", this.userId, this.pageId, getOnlineCount());
    }

    @ApiOperation("收到客户端消息后调用的方法")
    @OnMessage
    public void onMessage(String message) {
        log.info("来自用户ID为：{}的试题页面ID为：{}的用户的消息：{}", userId, pageId, message);
        //此处可以调用自定义发送消息方法，本系统暂时用不到
    }

    @ApiOperation("发生错误时调用的方法")
    @OnError
    public void onError(Throwable error) {
        log.error("发生错误", error);
    }

    @ApiOperation("发送评测结果给指定的用户的指定试题页面")
    public void sendResultMessage(Long receiveUserId, Long receivePageId, String jsonData) {
        log.info("待发送的消息（JSON格式）为：{}", jsonData);
        for (ProblemWebSocketService problemWebSocketService : WEB_SOCKET_SET) {
            if (receiveUserId.equals(problemWebSocketService.userId) && receivePageId.equals(problemWebSocketService.pageId)) {
                try {
                    problemWebSocketService.session.getBasicRemote().sendText(jsonData);
                } catch (IOException e) {
                    log.error("发送消息给用户{}的试题{}页面时出错！", problemWebSocketService.userId, problemWebSocketService.pageId, e);
                }
            }
        }
    }

    public static synchronized int getOnlineCount() {
        return onlineCount;
    }

    public static synchronized void addOnlineCount() {
        ProblemWebSocketService.onlineCount++;
    }

    public static synchronized void subOnlineCount() {
        ProblemWebSocketService.onlineCount--;
    }

}
